require('../js/global.js');
var http = require('http');
var url = require('url');
var exec = require('child_process').exec;
var Iconv = require('iconv').Iconv;
var ServerManager = require('../js/servers.js').ServerManager;
var csv = require('../js/csv.js');

var spliter = '\t';
var toolPath = '..';

global.gServers = new ServerManager();
var server = require('./server.js');

var gData = {
    incomes : {/* db : days : {} */  //收入
    },

    cashs : {/* db : days : uids: { actions:{} } */  //元宝消耗
    },

    shopCashs : {/* db : days : uids: { actions:{} } */  //商城元宝消耗
    },

    blackshopCashs :  {/* db : days : uids: { actions:{} } */  //黑市元宝消耗
    },

    init : function() {
        if( fs.existsSync('data/pay.json') ) {
            this.incomes = JSON.parse(fs.readFileSync('data/pay.json'));
        }
        if( fs.existsSync('data/cash.json') ) {
            this.cashs = JSON.parse(fs.readFileSync('data/cash.json'));
        }
        if( fs.existsSync('data/shop_cash.json') ) {
            this.shopCashs = JSON.parse(fs.readFileSync('data/shop_cash.json'));
        }
        if( fs.existsSync('data/blackshop_cash.json') ) {
            this.blackshopCashs = JSON.parse(fs.readFileSync('data/blackshop_cash.json'));
        }

        this._mergeServer();
    },

    _mergeServer : function() {
        return;

        var rawData = fs.readFileSync(name, 'utf8');
        var rows = rawData.split('\n');
        for (var line = 0; line < rows.length; ++line) {
            var servers = rows[line].split(' ');
            if (servers.length < 2) {
                continue;
            }

            INFO(util.format('merge data %j', servers));
            var dstSid = +servers[0];
            for (var i = 1; i < servers.length; ++i) {
                this.mergeCashData(+servers[i], dstSid);
            }
        }
        
        this.save();
        process.exit(0);
    },

    mergeCashData : function(srcSid, dstSid) {
        var datas = [this.cashs, this.shopCashs, this.blackshopCashs];
        datas.forEach(function(data) {
            var source = data[srcSid];
            var target = data[dstSid];

            for (var day in source) {
                var _source = source[day];
                if( !target[day] ) {
                    target[day] = _source;
                    continue;
                }
                _target = target[day];
                for( var seg in _source ) {
                    var _seg = _source[seg];
                    if( !_target[seg] ) {
                        _target[seg] = _seg;
                        continue;
                    }
                    _tseg = _target[seg];
                    for( var action in _seg ) {
                        if( !_tseg[action] ) {
                            _tseg[action] = _seg[action];    
                        }else{
                            _tseg[action].sum += _seg[action].sum;
                            _tseg[action].count += _seg[action].count;
                        }
                    }
                }
                delete data[srcSid];
            }
        });
    },

    save : function() {
        fs.writeFileSync('data/pay.json', JSON.stringify(this.incomes));
        fs.writeFileSync('data/cash.json', JSON.stringify(this.cashs));
        fs.writeFileSync('data/shop_cash.json', JSON.stringify(this.shopCashs));
        fs.writeFileSync('data/blackshop_cash.json', JSON.stringify(this.blackshopCashs));
    },

    calPay : function(sid, callback) {
        if( !this.incomes[sid] ) {
            this.incomes[sid] = {};
        }
        var income = this.incomes[sid];
        
        var serverStartTime = gDB.dbTimes[sid];
        var loader = new common.Loader(function(){
            callback && callback();
        });

        var strToday = (new Date()).format('yyyy-MM-dd');
        var yesToday = new Date(+new Date()-86400000).format('yyyy-MM-dd');
        for( var i=0; i<1000; i++ ) {
            var strDate = new Date(serverStartTime + 86400000*i).format('yyyy-MM-dd');
            if( strDate > strToday ) break;

            if( income.hasOwnProperty(strDate) ) continue;
            loader.addLoad(1);
            this._calPay(sid, strDate, strDate, function(){
                loader.onLoad(1);
            });
        }

        loader.addLoad(1);
        this._calPay(sid, yesToday, strToday, function(){
            loader.onLoad(1); 
        });
    },

    _calPay : function(sid, startDate, endDate, callback) {
        var condition = {'state':{'$in':[0,2]}};
        var minDay = startDate + ' 00:00:00';
        var maxDay = endDate + ' 23:59:59';
        condition['time'] = {'$gte':minDay, '$lt':maxDay};

        var data = {};
        var dbPay = gDB.getDB(sid).collection('pay');
        var cursor = dbPay.find(condition, {'time':1, 'amt':1});
        cursor.each(function(err, item){
            if( cursor.isClosed() ) {
                for( var strDate in data ) {
                    this.incomes[sid][strDate] = data[strDate] * config.platform.exchange_rate;
                }
                callback && callback(strDate);
            }else{
                if( !item ) return;
                item.amt = parseInt(item.amt);
                
                var strDate = item.time.substr(0, 10);
                data[strDate] = (data[strDate] || 0) + item.amt;
            }
        }.bind(this));
    },

    calPaid : function(sid, callback) {
        var dbPay = gDB.getDB(sid).collection('pay');
        dbPay.group({'openid':1}, {}, {sum:0}, "function(obj,prev){prev.sum += +obj.cash;}", 
        function(err, results){
            var paid = {};
            results.forEach(function(value){
                paid[value.openid] = value.sum;
            });
            
            var paidSegs = {};
            //var cashSegs = [500, 2000, 10000, 100000, 1000000000];
            var cashSegs = [];
            for( var uid in paid ) {
                for( var i=0; i<cashSegs.length; i++ ) {
                    var cashSeg = cashSegs[i];
                    if( paid[uid] <= cashSeg ) {
                        paidSegs[uid] = cashSeg;
                        break;
                    }
                }
            }
            findPlat(paidSegs);
        });

        function findPlat(paid) {
            var openids = Object.keys(paid);
            var newPaid = {};
            var dbPlat = gDB.getDB(sid).collection('plat');
            dbPlat.find({_id:{"$in":openids}}).toArray(function(err, results){
                var newPaid = {};
                results.forEach(function(item){
                    if( item._id in paid ) {
                        newPaid[item.uid] = paid[item._id];
                    }
                });
                callback && callback(newPaid);
            });
        }
    },

    calCash : function(sid, callback) {
        if( !this.cashs[sid] ) {
            this.cashs[sid] = {};
        }

        this.calPaid(sid, function(paid){
            var cash = this.cashs[sid];
            
            var serverStartTime = gDB.dbTimes[sid];
            var loader = new common.Loader(function(){
                callback && callback();
            });
            
            var strToday = (new Date()).format('yyyy-MM-dd');
            delete cash[strToday];
            for( var i=0; i<1000; i++ ) {
                var strDate = new Date(serverStartTime + 86400000*i).format('yyyy-MM-dd');
                if( strDate > strToday ) break;

                if( cash.hasOwnProperty(strDate) ) continue;
                loader.addLoad(1);
                this._calCash(sid, strDate, paid, function(){
                    loader.onLoad(1);
                });
            }
        }.bind(this));
    },

    _calCash : function(sid, strDate, paid, callback) {
        var condition = {};
        var minDay = strDate + ' 00:00:00';
        var maxDay = strDate + ' 23:59:59';
        condition['date'] = {'$gte':minDay, '$lt':maxDay};
        
        //var cashSegs = [500, 2000, 10000, 100000, 1000000000];
        var dbAnalysis = gDB.getDB(sid).collection('analysis');
        dbAnalysis.find(condition, {'action':1, 'uid':1, 'cash':1}).toArray(function(err, items){
            var data = {'500':{}, '2000':{}, '10000':{}, '100000':{}};
            items.forEach(function(item){
                var cashSeg = paid[item.uid] || '500'; 
                var actionData = data[cashSeg];
                if( !actionData ) {
                    actionData = {};
                    data[cashSeg] = actionData;
                }

                if( !(item.action in actionData) ) {
                    actionData[item.action] = {'sum':0, 'count':0};
                }

                actionData[item.action].sum += item.cash;
                actionData[item.action].count += 1;
            });

            this.cashs[sid][strDate] = data;
            callback && callback(true);
        }.bind(this));
    },

    calShopCash : function(sid, type, callback) {
        var cashs = {};
        if( type == 'shop' ) {
            cashs = this.shopCashs;
        }else if( type == 'black_shop' ) {
            cashs = this.blackshopCashs;
        }

        if( !cashs[sid] ) {
            cashs[sid] = {};
        }

        this.calPaid(sid, function(paid){
            var cash = cashs[sid];
        
            var serverStartTime = gDB.dbTimes[sid];
            var loader = new common.Loader(function(){
                callback && callback();
            });

            var strToday = (new Date()).format('yyyy-MM-dd');
            delete cash[strToday];
            for( var i=0; i<1000; i++ ) {
                var strDate = new Date(serverStartTime + 86400000*i).format('yyyy-MM-dd');
                if( strDate > strToday ) break;

                if( cash.hasOwnProperty(strDate) ) continue;
                loader.addLoad(1);
                this._calShopCash(sid, type, strDate, paid, function(){
                    loader.onLoad(1);
                });
            }
        }.bind(this));
    },


    _calShopCash : function(sid, action, strDate, paid, callback) {
        var condition = {};
        var minDay = strDate + ' 00:00:00';
        var maxDay = strDate + ' 23:59:59';
        condition['date'] = {'$gte':minDay, '$lt':maxDay};
        condition['action'] = action;

        var dbAnalysis = gDB.getDB(sid).collection('analysis');
        dbAnalysis.find(condition, {'c1':1, 'uid':1, 'cash':1}).toArray(function(err, items){
            var data = {'500':{}, '2000':{}, '10000':{}, '100000':{}};
            items.forEach(function(item){
                var cashSeg = paid[item.uid] || '500'; 
                var actionData = data[cashSeg];
                if( !actionData ) {
                    actionData = {};
                    data[cashSeg] = actionData;
                }

                if( !(item.c1 in actionData) ) {
                    actionData[item.c1] = {'sum':0, 'count':0};
                }

                actionData[item.c1].sum += item.cash;
                actionData[item.c1].count += 1;
            });

            if( action == 'shop' ) {
                this.shopCashs[sid][strDate] = data;
            }else if( action == 'black_shop' ) {
                this.blackshopCashs[sid][strDate] = data;
            }

            callback && callback(true);
        }.bind(this));
    },
};

function DBB() {
    this.dbs = [];
    this.dbNames = {};
    this.dbTimes = {};
    this.dbDates = {};
}

DBB.prototype = {
    init : function(){
        gServers.loadDB({slave:true, native:true}, function(err) {});
    },

    getDB : function(sid) {
        return gServers.getGameDB(sid);
    },

    checkDBs : function(callback) {
        gServers.loadGame(function(err) {
            var dbNames = {};
            var dbTimes = {};
            var dbDates = {};

            for (var sid in gServers.gameServers) {
                var server = gServers.gameServers[sid];
                if (!server.isValid()) {
                    continue;
                }

                var openDate = Date.createFromStamp(server.stamp);
                openDate.setHours(0);
                openDate.setMinutes(0);
                openDate.setSeconds(0);
                dbNames[sid] = server.dbName;
                dbTimes[sid] = +openDate;
                dbDates[sid] = server.time;
            }

            this.dbNames = dbNames;
            this.dbTimes = dbTimes;
            this.dbDates = dbDates;
            callback && callback();
        }.bind(this));
    }
}

var gDB = new DBB();
gDB.init();
gDB.checkDBs();
gData.init();

startWebServer();
setInterval(function(){
    gDB.checkDBs();
    gData.save();
}, 600*1000);

function startWebServer() {
    LOG('webserver start');
    server.loadConf();
    var webServer = http.createServer(function(req, res) {
        var reqObj = url.parse(req.url, true);
        var query = reqObj.query || {};
        var path = reqObj.pathname;

        if( req.method == 'POST' ) {
            req.connection.destroy();
            return;
        }

        if( !common.verifyGatewayAuth((new Date()).format('yyyy-MM-dd hh'), query.key) ) {
            res.writeHead(404, {"Content-Type": "text/plain"});
            res.end('405');
            return;
        }

        req.on('end', function() {
            if( path == '/pay' ) {
                handle_pay(res);
            }else if( path == '/cash' ) {
                if( query.type == 'all' ) {
                    handle_cash(query, res);
                }else if( query.type == 'shop' ) {
                    handle_shop_cash(query, 'shop', res);
                }else if( query.type == 'blackshop' ) {
                    handle_shop_cash(query, 'black_shop', res);
                }
            }else if( path == '/gamelog' ) {
                handle_game_log(query, res);
            }else if( path == '/online' ) {
                handle_online(query, res);
            }else if( path == '/cash_distribute' ) {
                   handle_cash_distribution(query, res);
            }else if( path == '/cash_analysis' ) {
                if( query.type == 'analysiscashshop' ) {
                    handle_analysis_cash_shop(query, res);
                }

                if( query.type == 'analysiscashtotal' ) {
                    handle_analysis_cash_total(query, res);
                }

                if( query.type == 'analysiscashblackshop' ) {
                    handle_analysis_cash_black_shop(query, res);
                }
            }else if( path == '/pay_log' ) {
               handle_pay_log(query, res);
            }else if( path == '/top_pay' ) {
               handle_top_pay(query, res);
            }else if( path == '/user' ) {
               handle_user(query, res);
            }else if( path == '/level' ) {
               handle_level(query, res);
            }else if( path == '/server_pay' ) {
               handle_server_pay(query, res);
            }else if( path == '/vip_distribute' ) {
               handle_vip_distribute(query, res);
            }else if( path == '/resource_left' ) {
               handle_resource_left(query, res);
            }else if( path == '/grouth' ) {
               handle_grouth(query, res);
            }else if( path == '/cash_log' ) {
               handle_cash_log(query, res);
            }else if( path == '/source' ) {
               handle_source(query, res);
            }else if( path == '/pay_list' ) {
               handle_pay_list(query, res);
            }else if( path == '/pet_level' ) {
               handle_pet_level(query, res);
            }else if( path == '/role' ) {
               handle_role(query, res);
            }else if( path == '/ride' ) {
               handle_ride(query, res);
            }else if( path == '/wing' ) {
               handle_wing(query, res);
            }else if( path == '/recover' ) {
               handle_recover(query, res);
            } else if( path == '/tools' ) {
               handle_tools(query, res);
            }else{
                res.writeHead(404, {"Content-Type": "text/plain"});
                res.end('404');
            }
        });

        req.resume();

    });
    
    webServer.listen(8001);
    webServer.timeout = 1200000;

    var pidFile = 'analysis.pid';
    process.on('SIGINT', function() {
        exit();
    });

    process.on('SIGTERM', function() {
        exit();
    });

    process.on('uncaughtException', function (err) {  
        console.error(err.stack);    
    });

    function exit() {
        LOG('analysis shutdown');
        fs.existsSync(pidFile) && fs.unlinkSync(pidFile);
        process.exit();
    }
}

function handle_pay(res){
    var loader = new common.Loader(display);
    for( var sid in gDB.dbNames ) {
        loader.addLoad(1);
        gData.calPay(sid, function(){
            loader.onLoad(1);
        });
    }
        
    function display() {
        var trs = [];
        var strToday = (new Date()).format('yyyy-MM-dd');
        var thisMonth = (new Date()).getMonth() + 1;
        var lastMonth = (new Date()).getMonth();           // 上月月份

        var nowDate = new Date();

        var curMonthDate = new Date(util.format("%d-%d-01 00:00:00", nowDate.getFullYear(), nowDate.getMonth() + 1));
        var lastMonthDate = common.getLastMonthSameDate(curMonthDate);

        var accountDay = 26;
        var curMonthAccountDate = new Date(util.format("%d-%d-%d 00:00:00", nowDate.getFullYear(), nowDate.getMonth() + 1, accountDay));
        if (nowDate.getDate() < accountDay) {
            curMonthAccountDate = common.getLastMonthSameDate(curMonthAccountDate);
        }
        var lastMonthAccountDate = common.getLastMonthSameDate(curMonthAccountDate);

        var curMonthAccountTotal = 0;
        var lastMonthAccountTotal = 0;

        var total = 0;
        var monthTotal = 0;
        var lastMonthTotal = 0; //上月总收入
        var weekTotals = {};    //全服每天全部收入
        weekTotals[strToday] = 0;
        var dayNums = 31;

        for( var i=1; i<=dayNums; i++ ) {
        //for( var i=40; i>0; i-- ) {
            var strDate = new Date(new Date()-86400000*i).format('yyyy-MM-dd');
            weekTotals[strDate] = 0;
        }
            
        var incomes = gData.incomes;
        for( var sid in incomes ) {
            var pays = incomes[sid];
            
            var curMonthAccountTotalByServer = 0;
            var lastMonthAccountTotalByServer = 0;

            var monthTotalByServer = 0;
            var lastMonthTotalByServer = 0;
            var dbTotal = 0;
            for( var strDate in pays ) {
                var payDate = new Date(strDate);
                var payAmt = pays[strDate];
               
                if (payDate >= curMonthDate) {
                    monthTotalByServer += payAmt;
                } else if (payDate >= lastMonthDate) {
                    lastMonthTotalByServer += payAmt;
                }

                if (payDate >= curMonthAccountDate) {
                    curMonthAccountTotalByServer += payAmt;
                } else if (payDate >= lastMonthAccountDate) {
                    lastMonthAccountTotalByServer += payAmt;
                }

                dbTotal += pays[strDate];
            }

            monthTotal += monthTotalByServer;
            lastMonthTotal += lastMonthTotalByServer;

            curMonthAccountTotal += curMonthAccountTotalByServer;
            lastMonthAccountTotal += lastMonthAccountTotalByServer;

            var tr = util.format('<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td>',
                sid, gDB.dbDates[sid], Math.round(dbTotal), Math.round(lastMonthTotalByServer), Math.round(monthTotalByServer),
                Math.round(pays[strToday] || 0), Math.round(lastMonthAccountTotalByServer), Math.round(curMonthAccountTotalByServer));

            for( var i=1; i<=dayNums; i++ ) {
            //for( var i=40; i>0; i-- ) {
                var strDate = new Date(new Date()-86400000*i).format('yyyy-MM-dd');
                tr += util.format('<td>%s</td>', Math.round(pays[strDate]) || 0 );
            }

            trs.push(tr);

            for( var strDate in weekTotals ) {
                weekTotals[strDate] += pays[strDate] || 0;
            }

            total += Math.round(dbTotal);
        }

        var th = util.format('<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td>',
                            '全部', '全部', total, Math.round(lastMonthTotal), Math.round(monthTotal),
                                Math.round(weekTotals[strToday]), 
                                Math.round(lastMonthAccountTotal), Math.round(curMonthAccountTotal));
        for( var i=1; i<=dayNums; i++ ) {
        //for( var i=40; i>0; i-- ) {
            var strDate = new Date(new Date()-86400000*i).format('yyyy-MM-dd');
            th += util.format('<td>%s</td>', Math.round(weekTotals[strDate]));
        }
        th += '</tr>';

        trs.splice(0,0,th);
        var content = '<table border="2"><tr><td>服务器</td><td>开服时间</td><td>总收入</td>'
                        + '<td>上月收入</td><td>当月收入</td><td>今日收入</td>'
                        + '<td>上月收入(26)</td><td>当月收入(26)</td>';
        for( var i=1; i<=dayNums; i++ ) {
        //for( var i=40; i>0; i-- ) {
            var strDate = new Date(new Date()-86400000*i).format('yyyy-MM-dd');
            content += util.format('<td>%s</td>', strDate);
        }
        content += '</tr>' + trs.join('')+'</table>'; 

        res.writeHead(200, common.htmlHeaders);
        res.end(content);
    }
}

function handle_shop_cash(req, type, res){
    var loader = new common.Loader(display);
    var sidStart = +req.sid_start;
    var sidEnd = +req.sid_end;

    var strToday = new Date().format('yyyy-MM-dd');
    var yesToday = new Date(+new Date()-86400000).format('yyyy-MM-dd');

    if( (req.end < yesToday) || (req.end == strToday) ) {
        display();
        return;
    }

    for( var sid=sidStart; sid<=sidEnd; sid++ ) {
        if( !(sid in gDB.dbNames) ) continue;
        loader.addLoad(1);
        gData.calShopCash(sid, type, function(){
            loader.onLoad(1);
        });
    }

    function display() {
        var cashData = gData.shopCashs;
        if( type == 'black_shop' ) {
            cashData = gData.blackshopCashs;
        }
        var data = {};
        var cashs = {'all':{'all':{'sum':0,'count':0}}};
        for( var sid in cashData ) {
            if( sid < sidStart || sid > sidEnd ) continue;
            var data = cashData[sid];
            for( var strDate in data ) {
                if( strDate < req.start || strDate > req.end ) continue;
                for( var seg in data[strDate] ) {
                    if( !(seg in cashs) ) {
                        cashs[seg] = {};
                        cashs[seg]['all'] = {'sum':0, 'count':0};
                    }

                    var items = data[strDate][seg];
                    for( var action in items ) {
                        var item = items[action];
                        if( !cashs[seg][action] ) {
                            cashs[seg][action] = {'sum':0, 'count':0};
                        }
                        cashs[seg][action].sum += item.sum;
                        cashs[seg][action].count += item.count;

                        if( !cashs['all'][action] ) {
                            cashs['all'][action] = {'sum':0, 'count':0};
                        }
                        cashs['all'][action].sum += item.sum;
                        cashs['all'][action].count += item.count;

                        cashs[seg]['all'].sum += item.sum;
                        cashs[seg]['all'].count += item.count;

                        cashs['all']['all'].sum += item.sum;
                        cashs['all']['all'].count += item.count;
                    }
                }
            }
        }

        var strToday = (new Date()).format('yyyy-MM-dd');
        for( var sid in cashData ) {
            if( sid < sidStart || sid > sidEnd ) continue;
            delete cashData[sid][strToday];
        }

        var viewArr = [];
        //var cashSegs = ['all', 500, 2000, 10000, 100000, 1000000000];
        var cashSegs = ['all'];
        for( var i=0; i<cashSegs.length; i++ ) {
            var seg = cashSegs[i];
            if( !(seg in cashs) ) continue;
            var sortArr = [];
            var c1s = cashs[seg];
            for( var c1 in c1s ) {
                var viewDataItem = c1s[c1];
                viewDataItem.seg = seg;
                viewDataItem.name = c1;
                if( c1 == 'all' ) {
                    name = '全部';
                }else if( type == 'shop' ) {
                    var confShop = gConfShop[c1];
                    if( !confShop ) continue;
                    var category1 =  confShop.Category1 ;
                    var category2 =  confShop.Category2 ;
                    var category3 =  confShop.Category3 ;

                    if( category1 == 'inventory' ) {
                        if( category2 == 'material' ) {
                            name = gConfMaterial[category3].Name;
                        }else if( category2 == 'gem' ){
                            name = gConfGem[category3].Name;
                        }
                    }else if( category1 == 'equip' ) {
                        name = gConfEquip[category3][category2.capWord()+'Name'];
                    }
                }else if( type == 'black_shop' ) {
                    var confBlackShop = gConfBlackShop[c1];
                    if( !confBlackShop ) continue;
                    var award = confBlackShop.Award1;
                    var awardType = award[0];
                    var awardId = award[1];
                    if( awardType == 'material' ) {
                        name = gConfMaterial[awardId].Name;
                    }else if( awardType == 'user' && awardId == 'gold' ) {
                        name = '金币';
                    }else if( awardType == 'user' && awardId == 'food' ) {
                        name = '粮草';
                    }else if( awardType == 'card' ) {
                        name = gConfRole[awardId].Name;
                    }
                }

                viewDataItem.name = name;
                sortArr.push(viewDataItem);
            }

            sortArr.sort(function(a, b) {
                return b.sum - a.sum;
            });

            viewArr = viewArr.concat(sortArr);
        }

        var htmlStr = '<table border=1>' +
            '<tr>'+
                '<td><b>区段</b></td>'+
                '<td><b>可消费名目</b></td>'+
                '<td><b>开始日期</b></td>'+
                '<td><b>结束日期</b></td>'+
                '<td><b>付费次数</b></td>'+
                '<td><b>元宝总数</b></td>'+
            '</tr>';

        for( var i=0, len=viewArr.length; i<len; i++ ) {
            var item = viewArr[i];
            htmlStr += ' <tr>'+
                '<td>'+ item.seg + '</td>'+
                '<td>'+ item.name + '</td>'+
                '<td>'+ req.start + '</td>'+
                '<td>'+ req.end + '</td>'+
                '<td>'+ item.count + '</td>'+
                '<td>'+ item.sum + '</td>'+
            '</tr>';
        }

        htmlStr += '</table>';

        res.writeHead(200, common.htmlHeaders);
        res.end(htmlStr);
    }
}

function handle_cash(req, res){
    var loader = new common.Loader(display);
    var sidStart = +req.sid_start;
    var sidEnd = +req.sid_end;
    
    var strToday = new Date().format('yyyy-MM-dd');
    var yesToday = new Date(+new Date()-86400000).format('yyyy-MM-dd');

    if( (req.end < yesToday) || (req.end == strToday) ) {
        display();
        return;
    }

    for( var sid=sidStart; sid<=sidEnd; sid++ ) {
        if( !(sid in gDB.dbNames) ) continue;
        loader.addLoad(1);
        gData.calCash(sid, function(){
            loader.onLoad(1);
        });
    }

    function display() {
        var htmlStr = '<table border=1>' +
            '<tr>'+
                '<td><b>区段</b></td>'+
                '<td><b>可消费名目</b></td>'+
                '<td><b>开始日期</b></td>'+
                '<td><b>结束日期</b></td>'+
                '<td><b>付费次数</b></td>'+
                '<td><b>元宝总数</b></td>'+
            '</tr>';

        var cashData = gData.cashs;
        
        var cashs = {'all':{'all':{'sum':0, 'count':0}}};
        for( var sid in cashData ) {
            if( sid < sidStart || sid > sidEnd ) continue;
            var data = cashData[sid];
            for( var strDate in data ) {
                if( strDate < req.start || strDate > req.end ) continue;
                for( var seg in data[strDate] ) {
                    if( !(seg in cashs) ) {
                        cashs[seg] = {};
                        cashs[seg]['all'] = {'sum':0, 'count':0};
                    }

                    var items = data[strDate][seg];
                    for( var action in items ) {
                        var item = items[action];
                        if( !cashs[seg][action] ) {
                            cashs[seg][action] = {'sum':0, 'count':0};
                        }
                        cashs[seg][action].sum += item.sum;
                        cashs[seg][action].count += item.count;

                        if( !cashs['all'][action] ) {
                            cashs['all'][action] = {'sum':0, 'count':0};
                        }
                        cashs['all'][action].sum += item.sum;
                        cashs['all'][action].count += item.count;

                        cashs[seg]['all'].sum += item.sum;
                        cashs[seg]['all'].count += item.count;

                        cashs['all']['all'].sum += item.sum;
                        cashs['all']['all'].count += item.count;
                    }
                }
            }
        }

        var strToday = (new Date()).format('yyyy-MM-dd');
        for( var sid in cashData ) {
            if( sid < sidStart || sid > sidEnd ) continue;
            delete cashData[sid][strToday];
        }

        var viewArr = [];
        //var cashSegs = ['all', 500, 2000, 10000, 100000, 1000000000];
        var cashSegs = ['all'];
        for( var i=0; i<cashSegs.length; i++ ) {
            var seg = cashSegs[i];
            if( !(seg in cashs) ) continue;
            var sortArr = [];
            var actions = cashs[seg];
            for( var action in actions ) {
                var viewDataItem = actions[action];
                viewDataItem.name = cashMap[action] || action;
                viewDataItem.seg = seg;
                sortArr.push(viewDataItem);
            }

            sortArr.sort(function(a, b) {
                return b.sum - a.sum;
            });

            viewArr = viewArr.concat(sortArr);
        }

        for( var i=0, len=viewArr.length; i<len; i++ ) {
            var item = viewArr[i];
            htmlStr += ' <tr>'+
                '<td>'+ item.seg + '</td>'+
                '<td>'+ item.name + '</td>'+
                '<td>'+ req.start + '</td>'+
                '<td>'+ req.end + '</td>'+
                '<td>'+ item.count + '</td>'+
                '<td>'+ item.sum + '</td>'+
                '</tr>';
        }

        htmlStr += '</table>';

        res.writeHead(200, common.htmlHeaders);
        res.end(htmlStr);
    }
}

function handle_game_log(query, res){
    var content = 'error';
    do {
        
        if( isNaN(query.server) || !query.openid ) {
            break;
        }

        var serverId = +query.server;
        var dbName = gDB.dbNames[serverId];
        if( !dbName ) {
            break;
        }

        var dbPlat = gDB.getDB(serverId).collection('plat');
        dbPlat.findOne({_id:query.openid}, {}, function(err, result){
            if( !err && result ) {
                var logDate = query.date;
                var logPage = query.page || 0;

                var cmd = util.format('%s/game querylog %d %d %d', toolPath, serverId, result.uid, logPage);

                if (logDate) {
                   cmd = util.format('%s "%s"', cmd, logDate); 
                }

                last = exec(cmd);
                var allData = '';
                last.stdout.on('data', function (data) {
                    allData += data;
                });

                last.stderr.on('data', function(data) {
                    allData += data;
                });

                last.on('exit', function (code) {
                    allData = allData.split('\n');
                    res.writeHead(200, common.htmlHeaders);
                    res.end(allData.join('<br/>'));
                });
            }
        });
        return;
    }while(false);

    res.writeHead(200, common.htmlHeaders);
    res.end(content);
}

function handle_online(query, res){
    var content = '参数错误';
    do {
        if( isNaN(query.sid) ) {
            break;
        }

        var serverId = +query.sid;
        var time = query.time;
        var segs = time.split(' ');
        var days = segs[0].split('-');
        var hours = segs[1].split(':');
        var year = parseInt(days[0]);
        var month = parseInt(days[1]) - 1;
        var day = parseInt(days[2]);
        var hour = parseInt(hours[0]);

        var serverStartTime = new Date(year, month, day, hour, 0, 0)
        var firstLeagueWarTime = new Date(serverStartTime - 0 + 86400000 * 1);
        firstLeagueWarTime = new Date(firstLeagueWarTime.getFullYear(), firstLeagueWarTime.getMonth(), firstLeagueWarTime.getDate(), 13, 0, 0);

        last = exec(util.format('%s/game open %d %d', toolPath, serverId, firstLeagueWarTime.getStamp()));

        var failed = false;
        var allData = '';

        last.stdout.on('data', function (data) {
            allData += data;
        });

        last.stderr.on('data', function(data) {
            allData += data;
            failed = true;
        });

        last.on('exit', function (code) {
            res.writeHead(200, common.htmlHeaders);
            if (code || failed) {
                res.end('上线2失败');
            } else {
                res.end('上线2成功');
            }
        });

        return;
    }while(false);

    res.writeHead(200, common.htmlHeaders);
    res.end(content);
}

function handle_pay_log(req, res){
    var content = 'error';
    res.writeHead(200, common.htmlHeaders);
    if( !req.sid || !req.openid ) {
        res.end(content);
        return;
    }

    var serverId = +req.sid;
    var dbName = gDB.dbNames[serverId];
    if( !dbName ) {
        res.end(content);
        return;
    }

    var dbPay = gDB.getDB(serverId).collection('pay');
    var data = [];
    var done = function(){
        var htmlStr = '<h1>'+req.sid+'服充值记录</h1> <table border="1">' +
            '<tr>'+
            '<td><b>充值时间</b></td>'+
            '<td><b>RMB</b></td>'+
            '<td><b>元宝</b></td>'+
            '<td><b>订单号</b></td>'+
            '</tr>';

        for( var i=0, len=data.length; i<len; i++ ) {
            htmlStr += ' <tr>'+
                '<td>'+ data[i].time + '</td>'+
                '<td>'+ data[i].rmb + '</td>'+
                '<td>'+ data[i].cash + '</td>'+
                '<td>'+ data[i].billno + '</td>'+
                '</tr>';
        }

        htmlStr += '</table>';

        res.end(htmlStr);
    };

    //dbPay.find({openid: req.openid}).sort([['time', -1]]).limit(1000).toArray(function(err, items) {
    dbPay.find({openid: req.openid}).sort([['time', -1]]).toArray(function(err, items) {
        for( var i=0, len=items.length; i<len; i++ ) {
            data.push({time: items[i].time, rmb: items[i].amt/100, cash: items[i].cash, billno: items[i].billno});
        }

        done();
    });
}

function handle_top_pay(req, res) {
    var content = 'error';
    res.writeHead(200, common.htmlHeaders);
    if( !req.sid ) {
        res.end(content);
        return;
    }

    var serverId = +req.sid;
    var dbName = gDB.dbNames[serverId];
    if( !dbName ) {
        res.end(content);
        return;
    }
    
    var db = gDB.getDB(serverId);
    var dbPay = db.collection('pay');
    var dbPlat = db.collection('plat');
    var dbUser = db.collection('user');

    var done = function(){
        var htmlStr = '<h1>' + req.sid +'服充值总排行(红色为滚服玩家)</h1> <table border="1">' +
            '<tr>'+
            '<td><b>玩家openid</b></td>'+
            '<td><b>玩家名</b></td>'+
            '<td><b>总的充值</b></td>'+
            '</tr>';

        for( var i=0, len=data.length; i<len; i++ ) {
            htmlStr += ' <tr>';
            if( data[i].roll ) {
                htmlStr += '<td style="color:red">';
            }else{
                htmlStr += '<td>';
            }

            htmlStr += data[i].openid + '</td>'+
                '<td>'+ data[i].name + '</td>'+
                '<td>'+ data[i].sum + '</td>'+
                '</tr>';
        }

        htmlStr += '</table>';

        res.end(htmlStr);
    }

    var data = [];
    dbPay.group({'openid':1}, {}, {sum:0}, "function(obj,prev){prev.sum += +obj.amt;}", 
        function(err, results){
            var users = results.sort(function(a,b){return b.sum - a.sum;}).slice(0, 100);
            var openids = [];
            users.forEach(function(value){
                openids.push(value.openid);
                value.sum /= 100;
            });
            
            var uids = [];
            var openid_uids = {};
            var names = {};
            var rolls = {};
            var cursor = dbPlat.find({_id:{"$in":openids}}, {});
            cursor.each(function(err, item){
                if( cursor.isClosed() ) {
                    var cursor2 = dbUser.find({_id:{"$in":uids}},{'info':1});
                    cursor2.each(function(err, item){
                        if( cursor2.isClosed() ) {
                            for(var i=0; i<users.length; i++) {
                                var user = users[i];
                                user.name = names[openid_uids[user.openid]];
                                user.roll = rolls[openid_uids[user.openid]];
                            }
                            data = users;
                            done();
                        }else{
                            if( !item ) return;
                            names[item._id] = item.info.un;
                            rolls[item._id] = item.info.roll ? 1:0;
                        }
                    });
                }else{
                    if( !item ) return;
                    uids.push(item.uid);
                    openid_uids[item._id] = item.uid;
                }
            });
        });
}

function handle_user(req, res) {
    var content = 'error';
    res.writeHead(200, common.htmlHeaders);
    if( !req.sid ) {
        res.end(content);
        return;
    }

    var serverId = +req.sid;
    var dbName = gDB.dbNames[serverId];
    if( !dbName ) {
        res.end(content);
        return;
    }

    var dbPlat = gDB.getDB(serverId).collection('plat');
    var dbUser = gDB.getDB(serverId).collection('user');

    var data = {count:8};
    data.done = function(){
        this.count -= 1;
        if( this.count <= 0 ) {
            var lines = [];
            lines.push(util.format('最近一小时新用户:%d', data.hu));
            lines.push(util.format('总用户:%d, 广点通 => %d, 任务集市 => %d, 默认 => %d, 3366 => %d', data.u.total, data.u.gdt || 0, data.u.qq_task || 0, data.u.default || 0, data.u['3366'] || 0));
            lines.push(util.format('活跃用户:%d', data.au));
            lines.push(util.format('新增用户:%d', data.nu));
            lines.push(util.format('有效用户(过新手引导):%d', data.tu));
            lines.push(util.format('滚服用户:%d', data.ru));

            lines.push('最近每15分钟新用户:');

            var mins = Object.keys(data.mu);
            mins.sort();
            mins.forEach(function(min){
                lines.push( util.format("%s => %d, 广点通 => %d, 任务集市 => %d, 默认 => %d, 3366 => %d", min, data.mu[min], data.mut[min]['gdt']||0, data.mut[min]['qq_task']||0, data.mut[min]['default']||0, data.mut[min]['3366']||0) );
            });

            lines.push('每日新增有效用户:');
            data.dtu.forEach(function(item){
                lines.push(util.format("%s => %d, 广点通 => %d, 任务集市 => %d, 默认 => %d, 3366 => %d", item.day, item.total, item.gdt || 0, item.qq_task || 0, item.default || 0, item['3366'] || 0));
            });

            res.writeHead(200, common.defaultHeaders);
            res.end(lines.join('\n'));
        }
    };

    dbUser.group({'info.via':1},{},{'num':0},"function(obj,prev){prev.num += 1;}", function(err, results){
        var us = {};
        var total = 0;
        results.forEach(function(item){
            total += item.num;
            if( !item['info.via'] ) {
                item['info.via'] = 'default';
            }
            us[item['info.via']] = ( us[item['info.via']] || 0 ) + item.num;
        });
        us.total = total;
        data.u = us;
        data.done();
    });
    
    dbPlat.count({'time':{$gt:+new Date()/1000-3600}}, function(err, count){
        data.hu = count;
        data.done();
    });

    dbUser.count({'mark.tutorial':0}, function(err, count){
        data.tu = count;
        data.done();
    });

    dbUser.count({'info.roll':1}, function(err, count){
        data.ru = count;
        data.done();
    });

    data.mu = {};
    data.mut = {};
    var cursor = dbUser.find({'info.create':{$gt:+new Date()/1000-36000}},{'info.create':1, 'info.via':1});
    cursor.each(function(err, item){

        if( cursor.isClosed() ) {
            data.done();
        }

        if( !item) return;

        var min = new Date(Math.floor( item.info.create / 900 )*900*1000).format('yyyy-MM-dd hh:mm:ss');
        if( !data.mu[min] ) {
            data.mu[min] = 0;
        }
        data.mu[min] += 1;

        if( !data.mut[min] ) {
            data.mut[min] = {};
        }
        if( item.info.via == '' ) {
            item.info.via = 'default';
        }
        if( !data.mut[min].hasOwnProperty(item.info.via) ) {
            data.mut[min][item.info.via] = 0;
        }else{
            data.mut[min][item.info.via] += 1;
        }
    });

    var today = common.getDate();
    dbUser.count({'mark.day':today}, function(err, count){
        data.au = count;
        data.done();
    });

    var now = new Date();
    var time = Math.round((+new Date(now.getFullYear(), now.getMonth(), now.getDate()))/1000);
    dbPlat.count({'time':{$gt:time}}, function(err, count){
        data.nu = count;
        data.done();
    });


    var keyf=function(doc){
        var dateTime = new Date(doc.info.create*1000);
        var via = doc.info.via;
        if( !via ) {
            via = 'default';
        }
        return {"day":[dateTime.getFullYear(), dateTime.getMonth()+1, dateTime.getDate()].join('-'), 'via':via};
    };
    var valid_level=6;
    var condition = {"mark.tutorial":0};
    var reduce=function(doc,prev){
        ++prev.num;
    };

    dbUser.group(keyf,condition,{"num":0},reduce,function(err,results){
        var days = {};
        results.forEach(function(item){
            if( !days[item.day] ) {
                days[item.day] = {};
            }

            days[item.day][item.via] = (days[item.day][item.via] || 0) + item.num;
        });
        
        var adays = [];
        for( var day in days ) {
            var total = 0;
            for( var via in days[day] ) {
                total += days[day][via];
            }

            days[day].total = total;
            days[day].day = day;
            adays.push(days[day]);
        }

        adays.sort(function(a,b){return a.day < b.day});
        data.dtu = adays;
        data.done();
    });

}

function handle_level(req, res) {
    var content = 'error';
    res.writeHead(200, common.htmlHeaders);
    if( !req.sid ) {
        res.end(content);
        return;
    }

    var serverId = +req.sid;
    var dbName = gDB.dbNames[serverId];
    if( !dbName ) {
        res.end(content);
        return;
    }

    var data = {};
    data.done = function(){
        if(data.level && data.tutorial && data.tlevel){
            var html = data.level + data.tlevel + data.tutorial;
            res.writeHead(200, common.htmlHeaders);
            res.end(html);
        }
    }

    var dbUser = gDB.getDB(serverId).collection('user');

    dbUser.group(['status.level'], {}, {sum:0}, "function(doc,prev){prev.sum += 1;}", 
        function(err, results){
            results.sort(function(a, b){return a['status.level'] - b['status.level'];})

            var html = '<h3>'+serverId +'服等级分布</h3><table border=1 cellpadding=5>';
            html += '<tr><td><b>等级</b></td><td><b>人数<b></td></tr>';
            for( var i=0; i<results.length; i++ ) {
                var item = results[i];
                html += util.format('<tr><td>%s</td><td>%s</td></tr>', item['status.level'], item.sum);
            }
            html += '</table>';
            data.level = html; 
            data.done();
        }
    );

    dbUser.group(['status.level','info.via'], {}, {sum:0}, "function(doc,prev){prev.sum += 1;}", 
        function(err, results){
            var tdata = {};
            results.forEach(function(item){
                if( !tdata[item['status.level']] ) {
                    tdata[item['status.level']] = {};
                }

                if( item['info.via'] == '' ) {
                    item['info.via'] = 'default';
                }
                tdata[item['status.level']][item['info.via']] = (tdata[item['status.level']][item['info.via']] || 0) + item.sum;
            });

            var html = '<h3>'+serverId +'服等级分布</h3><table border=1 cellpadding=5>';
            html += '<tr><td><b>等级</b></td><td><b>总数</b></td><td><b>广点通</b></td><td><b>任务集市</b></td><td><b>默认</b></td><td><b>3366</b></td></tr>';
            for( var level in tdata ) {
                var item = tdata[level];
                var total = (item.gdt || 0) + (item.qq_task || 0) + (item.default || 0) + (item['3366'] || 0);
                html += util.format('<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>', level, total, item.gdt || 0, item.qq_task|| 0, item.default||0, item['3366'] || 0);
            }
            html += '</table>';
            data.tlevel = html; 
            data.done();
        }
    );
    
    dbUser.group({'mark.tutorial':1}, {}, {sum:0}, "function(doc,prev){prev.sum += 1;}",
        function(err, results){
            results.sort(function(a, b){return a['mark.tutorial'] - b['mark.tutorial'];})
            var html = '<h3>'+serverId +'服新手引导步骤分布</h3><table border=1 cellpadding=5>';
            html += '<tr><td><b>步骤</b></td><td><b>人数<b></td></tr>';
            for( var i=0; i<results.length; i++ ) {
                var item = results[i];
                html += util.format('<tr><td>%s</td><td>%s</td></tr>', item['mark.tutorial'], item.sum);
            }
            html += '</table>';
            data.tutorial = html; 
            data.done();
    });
}

function handle_server_pay(req, res) {
    var content = 'error';
    res.writeHead(200, common.htmlHeaders);
    if( !req.sid ) {
        res.end(content);
        return;
    }

    var serverId = +req.sid;
    var dbName = gDB.dbNames[serverId];
    if( !dbName ) {
        res.end(content);
        return;
    }

    var dbPay = gDB.getDB(serverId).collection('pay');
    var dbPlat = gDB.getDB(serverId).collection('plat');
    var dbUser = gDB.getDB(serverId).collection('user');
    //dbPay.group({}, {$or:[{'state':0},{'state':2}]}, {amt:0,user:{},day:{}}, "function(obj, prev){ prev.amt += +obj.amt; prev.user[obj.openid] = 1; var date = obj.time.substr(0, 10); if( !prev.day[date] ) {prev.day[date] = 0;} prev.day[date] += +obj.amt;}", 
    dbPay.find({$or:[{'state':0},{'state':2}]}).toArray(function(err, results){
        var openids = {};
        results.forEach(function(item){
            openids[item.openid] = 1; 
        });

        dbPlat.find({_id:{'$in':Object.keys(openids)}},{uid:1}).toArray(function(err, plats){
            var uidOpenids = {};
            var uids = [];
            plats.forEach(function(item){
                uidOpenids[item.uid] = item._id;
                uids.push(item.uid);
            });

            dbUser.find({_id:{'$in':uids}, 'info.roll':1}, {_id:1}).toArray(function(err, users){
                var rolls = {};
                users.forEach(function(item){
                    rolls[uidOpenids[item._id]] = 1; 
                });

                for( var openid in openids ) {
                    if( !(openid in rolls) ) {
                        rolls[openid] = 0;
                    }
                }
                
                var days = {};
                var total = 0;
                results.forEach(function(item){
                    item.amt = +item.amt;
                    var day = item.time.substr(0, 10);
                    if( !days[day] ) {
                        days[day] = {
                            'roll': 0,
                            'unroll': 0,
                        };
                    }
                    if( rolls[item.openid] ) {
                        days[day].roll += item.amt;
                    }else{
                        days[day].unroll += item.amt;
                    }

                    total += item.amt;
                });

                var paycash = total/100;
                var paycount = Object.keys(openids).length;

                var sdays = [];
                for( var day in days ) {
                    var obj = days[day];
                    obj.day = day;
                    sdays.push(obj);
                }
                sdays.sort(function(a, b){return a.day < b.day ? 1 : -1;});

                var html = '<h3>'+serverId +'服充值收入</h3>';
                html += util.format('<p>充值人数: %s 滚服: %s 非滚服: %s</p>', paycount, users.length, (Object.keys(openids).length - users.length));
                html += util.format('<p>总收入人民币: %s</p>', paycash);
                html += '<p>收入详情:</p>';
                html += '<table border=1 cellpadding=5><tr><td><b>日期</b></td><td><b>滚服收入<b></td><td><b>非滚服收入<b></td></tr>';

                sdays.forEach(function(obj){
                    html += util.format('<tr><td>%s</td><td>%s</td><td>%s</td></tr>', obj.day, (obj.roll||0)/100, (obj.unroll||0)/100);
                });
                html += '</table>';

                res.writeHead(200, common.htmlHeaders);
                res.end(html);
            });
        });
    });
}

function handle_vip_distribute(req, res) {
    var content = 'error';
    res.writeHead(200, common.htmlHeaders);
    if( !req.sid ) {
        res.end(content);
        return;
    }

    var serverId = +req.sid;
    var dbName = gDB.dbNames[serverId];
    if( !dbName ) {
        res.end(content);
        return;
    }

    var dbUser = gDB.getDB(serverId).collection('user');
    var vip = {};
    var done = function(){
        res.writeHead(200, common.htmlHeaders);
                
        var htmlStr = '<h1>'+ req.sid +'服VIP分布</h1> <table border="1">'+ 
            ' <tr>'+
                '<td><b>等级</b></td>'+
                '<td><b>人数<b></td>'+
                '</tr>';
        for( var level in vip ) {
            htmlStr += ' <tr>'+
                '<td>'+ level + '</td>'+
                '<td>'+ vip[level] + '</td>'+
                '</tr>';
        }    
     
        htmlStr += '</table>';
        res.end(htmlStr);
    };   

    dbUser.group(['status.vip'],{'status.vip' :{'$gt' : 0}},{'sum': 0}, 'function(obj, prev) {prev.sum ++;}',
        function(err, results) {
            results.forEach(function(item){
                vip[item['status.vip']] = item.sum;
            });
        done();
    });
}

function handle_resource_left(req, res) {
    var content = 'error';
    res.writeHead(200, common.htmlHeaders);
    if( !req.sid ) {
        res.end(content);
        return;
    }

    var serverId = +req.sid;
    var dbName = gDB.dbNames[serverId];
    if( !dbName ) {
        res.end(content);
        return;
    }

    var dbUser = gDB.getDB(serverId).collection('user');
    var cashLeft = 0;
    var done = function(){
        var htmlStr = '<h1>'+req.sid+'服剩余元宝总数:'+ cashLeft + '</h1>'  ; 
           
        res.end(htmlStr);
    };

    dbUser.group([],{},{cash: 0, gold: 0, food: 0, honor: 0}, 'function(obj, prev) {prev.cash += obj.status.cash; prev.gold += obj.status.gold; prev.food += obj.status.food; prev.honor += obj.status.honor;}',
        function(err, results) {
            var lefts = results[0];
            var htmlStr = '<h1>'+req.sid+'服剩余资源总数</h1> <table border="1">'+
                ' <tr>'+
                    '<td><b>资源</b></td>'+
                    '<td><b>剩余总数</b></td>'+
                '</tr>'+
                ' <tr>'+
                    '<td>元宝</td>'+
                    '<td>' + lefts.cash +'</td>'+
                '</tr>'+
                '<tr>'+
                    '<td>金币</td>'+
                    '<td>' + lefts.gold +'</td>'+
                '</tr>'+
                ' <tr>'+
                    '<td>粮草</td>'+
                    '<td>' + lefts.food +'</td>'+
                '</tr>'+
                ' <tr>'+
                    '<td>军功</td>'+
                    '<td>' + lefts.honor +'</td>'+
                '</tr>'+
            '</table>';

            res.end(htmlStr);
        });
}

function handle_cash_distribution(req, res) {
    var content = 'error';
    res.writeHead(200, common.htmlHeaders);
    if( !req.sid ) {
        res.end(content);
        return;
    }

    var serverId = +req.sid;
    var dbName = gDB.dbNames[serverId];
    if( !dbName ) {
        res.end(content);
        return;
    }

    var dbAnalysis = gDB.getDB(serverId).collection('analysis');
    var data = {};
    var done = function(){
        res.writeHead(200, common.htmlHeaders);

        var viewData = getIntervalMap(data);
        var htmlStr = '<h1>' + req.sid +'服元宝花费分布</h1> <table border="1">'+
            ' <tr>'+
                '<td><b>区间</b></td>'+
                '<td><b>人数</b></td>'+
            '</tr>';

        for( var interval in viewData ) {
            htmlStr += ' <tr>'+
                '<td>'+ interval + '</td>'+
                '<td>'+ viewData[interval] + '</td>'+
            '</tr>';
        }

        htmlStr += '</table>';

        res.end(htmlStr);
    };

    dbAnalysis.group(['uid'], {}, {sum:0}, "function(obj, prev){prev.sum += +obj.cash}", 
        function(err, results) {
            results.forEach(function(item){
                data[item.uid] = item.sum;
            });
        done();
    });
}

function handle_analysis_cash_black_shop(req, res) {
    var content = 'error';
    res.writeHead(200, common.htmlHeaders);
    if( !req.sid ) {
        res.end(content);
        return;
    }

    var serverId = +req.sid;
    var dbName = gDB.dbNames[serverId];
    if( !dbName ) {
        res.end(content);
        return;
    }

    var dbAnalysis = gDB.getDB(serverId).collection('analysis');
    var data = {};
    var done = function(){
        res.writeHead(200, common.htmlHeaders);

        var viewData = {};
        var interval = [1, 100000000];
        if( req.interval ) {
            interval = req.interval.toString().split('-');
        }

        var items = data.items;
        var users = data.users;
        for( var i=0,len=items.length; i<len; i++ ) {
            var item = items[i];
            var id = item.c1;
            if( !gConfBlackShop[id] ) continue; 

            var confBlackShop = gConfBlackShop[id];
            var uid = item.uid;
            var currency = confBlackShop.Currency;
            var award = confBlackShop.Award1;
            var awardType = award[0];
            var awardId = award[1];
            
            if( users[uid].cash >= +interval[0] && users[uid].cash <= +interval[1] ) {
                if( currency == 'cash' && awardType == 'material' ) {
                    var shopName = gConfMaterial[awardId].Name;
                    var viewItem = viewData[shopName];
                    if( !viewItem ) {
                        viewItem = {count: 0, cash: 0};
                        viewData[shopName] = viewItem;
                    }

                    viewItem.count += item.count;
                    viewItem.cash += item.sum;
                }
            }
        }

        var htmlStr = '<h1>'+req.sid+'服黑市' + interval[0] + '-'+ interval[1] + '</h1> <table border="1">' +
            '<tr>'+
                '<td><b>可消费名目</b></td>'+
                '<td><b>开始日期</b></td>'+
                '<td><b>结束日期</b></td>'+
                '<td><b>付费次数</b></td>'+
                '<td><b>元宝总数</b></td>'+
            '</tr>';

        var viewArr = [];
        for( var name in viewData ) {
            var viewDataItem = viewData[name];
            viewDataItem.name = name;
            if( viewDataItem.cash <= 0 ) continue;
            viewArr.push(viewDataItem);
        }

        viewArr.sort(function(a, b) {
            return b.cash - a.cash;
        });

        for( var i=0, len=viewArr.length; i<len; i++ ) {
            var item = viewArr[i];
            htmlStr += ' <tr>'+
                '<td>'+ item.name + '</td>'+
                '<td>'+ req.start.substr(0,10) + '</td>'+
                '<td>'+ req.end.substr(0,10) + '</td>'+
                '<td>'+ item.count + '</td>'+
                '<td>'+ item.cash + '</td>'+
                '</tr>';
        }

        htmlStr += '</table>';

        res.end(htmlStr);
    };

    req.start += ' 00:00:00';
    req.end += ' 23:59:59';

    cashData(data, req, dbAnalysis, function( ) {
        dbAnalysis.group(['uid','c1'], {'action': 'black_shop', 'date':{'$gte':req.start, '$lte': req.end}}, {sum:0,count:0}, "function(obj,prev){prev.sum += +obj.cash; prev.count += 1;}",
            function(err, results){
                data.items = results;
                done();
        });
    });
}

function handle_analysis_cash_shop(req, res) {
    var content = 'error';
    res.writeHead(200, common.htmlHeaders);
    if( !req.sid ) {
        res.end(content);
        return;
    }

    var serverId = +req.sid;
    var dbName = gDB.dbNames[serverId];
    if( !dbName ) {
        res.end(content);
        return;
    }

    var dbAnalysis = gDB.getDB(serverId).collection('analysis');
    var data = {};
    var done = function(){
        res.writeHead(200, common.htmlHeaders);

        var viewData = {};
        var interval = [1, 100000000];
        if( req.interval ) {
            interval = req.interval.toString().split('-');
        }

        var items = data.items;
        var users = data.users;
        for( var i=0,len=items.length; i<len; i++ ) {
            var item = items[i];
            var id = item.c1;
            if( !gConfShop[id] ) continue; 

            var confShop = gConfShop[id];
            var uid = item.uid;
            if( users[uid].cash >= +interval[0] && users[uid].cash <= +interval[1] ) {
                var category1 =  confShop.Category1 ;
                var category2 =  confShop.Category2 ;
                var category3 =  confShop.Category3 ;

                var viewItem = {count: 0, cash: 0};
                if( category1 == 'inventory' ) {
                    if( category2 == 'material' ) {
                        var materialName = gConfMaterial[category3].Name;
                        viewItem = viewData[materialName];
                        if( !viewItem ) {
                            viewItem = {count: 0, cash: 0};
                            viewData[materialName] = viewItem;
                        };
                    }else if( category2 == 'gem' ){
                        var gemName = gConfGem[category3].Name;
                        viewItem = viewData[gemName];
                        if( !viewItem ) {
                            viewItem = {count: 0, cash: 0};
                            viewData[gemName] = viewItem;
                        };
                    }
                }else if( category1 == 'equip' ) {
                    var equipName = gConfEquip[category3][category2.capWord()+'Name'];
                    viewItem = viewData[equipName];
                    if( !viewItem ) {
                        viewItem = {count: 0, cash: 0};
                        viewData[equipName] = viewItem;
                    };
                }

                viewItem.count += item.count;
                viewItem.cash += item.sum;
            }
        }

        var htmlStr = '<h1>'+req.sid+'服商城' + interval[0] +  '-'+ interval[1] + '</h1> <table border="1">' +
            '<tr>'+
                '<td><b>可消费名目</b></td>'+
                '<td><b>开始日期</b></td>'+
                '<td><b>结束日期</b></td>'+
                '<td><b>付费次数</b></td>'+
                '<td><b>元宝总数</b></td>'+
            '</tr>';

        var viewArr = [];
        for( var name in viewData ) {
            var viewDataItem = viewData[name];
            viewDataItem.name = name;
            if( viewDataItem.cash <= 0 ) continue;
            viewArr.push(viewDataItem);
        }

        viewArr.sort(function(a, b) {
            return b.cash - a.cash;
        });

        for( var i=0, len=viewArr.length; i<len; i++ ) {
            var item = viewArr[i];
            htmlStr += ' <tr>'+
                '<td>'+ item.name + '</td>'+
                '<td>'+ req.start.substr(0,10) + '</td>'+
                '<td>'+ req.end.substr(0,10) + '</td>'+
                '<td>'+ item.count + '</td>'+
                '<td>'+ item.cash + '</td>'+
                '</tr>';
        }

        htmlStr += '</table>';
        res.end(htmlStr);
    };

    cashData(data, req, dbAnalysis, function( ) {
        dbAnalysis.group(['uid','c1'], {action: 'shop', 'date':{'$gte':req.start, '$lte': req.end}}, {sum:0,count:0}, "function(obj,prev){prev.sum += +obj.cash; prev.count += 1;}",
            function(err, results){
                data.items = results;
                done();
        });
    });
}

function handle_analysis_cash_total(req, res) {
    var content = 'error';
    res.writeHead(200, common.htmlHeaders);
    if( !req.sid ) {
        res.end(content);
        return;
    }

    var serverId = +req.sid;
    var dbName = gDB.dbNames[serverId];
    if( !dbName ) {
        res.end(content);
        return;
    }

    var dbAnalysis = gDB.getDB(serverId).collection('analysis');
    var data = {};
    var done = function(){
        res.writeHead(200, common.htmlHeaders);
    
        var viewData = {};
        var interval = [1, 100000000];
        if( req.interval ) {
            interval = req.interval.toString().split('-');
        }
        
        var items = data.items;
        var users = data.users;
        for( var i=0,len=items.length; i<len; i++ ) {
            var item = items[i];
            var uid = item.uid;
            if( users[uid].cash >= +interval[0] && users[uid].cash <= +interval[1]) {
                var actionName = cashMap[item.action] || item.action;
                if( !(actionName in viewData) ) {
                    viewData[actionName] = {count: 0, cash: 0};
                }

                viewData[actionName].count += item.count;
                viewData[actionName].cash += item.sum;
            }
        }

        var htmlStr = '<h1>'+req.sid+'服总体' + interval[0] +  '-'+ interval[1] + '</h1> <table border="1">' +
            '<tr>'+
                '<td><b>可消费名目</b></td>'+
                '<td><b>开始日期</b></td>'+
                '<td><b>结束日期</b></td>'+
                '<td><b>付费次数</b></td>'+
                '<td><b>元宝总数</b></td>'+
            '</tr>';

        var viewArr = [];
        for( var name in viewData ) {
            var viewDataItem = viewData[name];
            viewDataItem.name = name;
            if( viewDataItem.cash <= 0 ) continue;
            viewArr.push(viewDataItem);
        }

        viewArr.sort(function(a, b) {
            return b.cash - a.cash;
        });

        for( var i=0, len=viewArr.length; i<len; i++ ) {
            var item = viewArr[i];
            htmlStr += ' <tr>'+
                '<td>'+ item.name + '</td>'+
                '<td>'+ req.start.substr(0,10) + '</td>'+
                '<td>'+ req.end.substr(0,10) + '</td>'+
                '<td>'+ item.count + '</td>'+
                '<td>'+ item.cash + '</td>'+
                '</tr>';
        }

        htmlStr += '</table>';

        res.end(htmlStr);
    };

    cashData(data, req, dbAnalysis, done);
}

function cashData(data, req, dbAnalysis, callback) {
    req.start += ' 00:00:00';
    req.end += ' 23:59:59';
    dbAnalysis.group(['uid','action'], {'date':{'$gte':req.start, '$lte': req.end}}, {sum:0,count:0}, "function(obj,prev){prev.sum += +obj.cash; prev.count += 1;}",
        function(err, results){
            data.users = {};
            data.items = results;
            results.forEach(function(item){
                var userData = data.users[item.uid];
                if( !userData ) {
                    userData = {'cash':0};
                    data.users[item.uid] = userData;
                }

                userData.cash += +item.sum;
            });
            callback && callback();
        });
}

function handle_source(req, res) {
    var content = 'error';
    res.writeHead(200, common.htmlHeaders);
    if( !req.sid ) {
        res.end(content);
        return;
    }

    var serverId = +req.sid;
    var dbName = gDB.dbNames[serverId];
    if( !dbName ) {
        res.end(content);
        return;
    }

    var condition = {};
    if( req.time == 5 ) {
        condition['active_time'] = {$gte:0, $lte:300};
    }else if( req.time == 10 ) {
        condition['active_time'] = {$gt:300, $lte:600};
    }

    var dbUser = gDB.getDB(serverId).collection('user');
    dbUser.group(['info.via'],condition,{count:0}, 'function(obj, prev) {prev.count += 1;}',
        function(err, results) {
            var htmlStr = '<h1>'+req.sid+'服来源<' + req.time +'分钟分布</h1> <table border="1">'+
                ' <tr>'+
                    '<td><b>来源</b></td>'+
                    '<td><b>数量</b></td>'+
                '</tr>';
            results.forEach(function(obj){
                htmlStr += util.format('<tr><td>%s</td><td>%s</td></tr>', obj['info.via'], obj.count);
            });

            htmlStr += '</table>';

            res.end(htmlStr);
        });
}

function handle_pay_list(req, res) {
    res.writeHead(200, common.htmlHeaders);

    var segs1 = [100, 500, 1000, 2000, 3000];
    var segs2 = [3000, 4000, 5000, 8000, 10000, 20000, 30000, 50000, 80000, 100000, 200000, 300000, 500000, 800000, 1000000, 10000000];

    var paid = {};
    var segCount = {};

    var loader = new common.Loader(display);
    for( var sid in gDB.dbNames ) {
        loader.addLoad(1);
        calPay(sid, function(){
            loader.onLoad(1);
        });
        
        for( var j=0; j<segs1.length; j++ ) {
            var max = segs1[j];
            var min = j > 0 ? segs1[j-1] : 1;

            segCount[max] = 0;
            loader.addLoad(1);
            countPay(sid, min, max, function(){
                loader.onLoad(1);
            });
        }
    }

    function calPay(sid, callback) {
        var serverId = sid;
        var dbName = gDB.dbNames[serverId];
        var db = gDB.getDB(serverId);

        var dbUser = db.collection('user');
        dbUser.find({'activity.paid':{$gte:3000}}, {_id:1, 'activity.paid':1}).toArray(function(err, results){
            results.forEach(function(obj){
                paid[obj._id] = obj.activity.paid;
            });
            callback && callback();
        });
    }

    function countPay(sid, min, max, callback) {
        var serverId = sid;
        var dbName = gDB.dbNames[serverId];
        var db = gDB.getDB(serverId);
        var dbUser = db.collection('user');

        dbUser.count({'activity.paid':{$gte:min, $lt:max}}, function(err, results){
            segCount[max] += results;
            callback && callback();
        });
    }


    function display() {
        var htmlStr = '<h1>充值分布</h1> <table border="1">' +
            '<tr>'+
            '<td><b>充值元宝</b></td>'+
            '<td><b>数量</b></td>'+
            '</tr>';

        for( var i=0; i<segs1.length; i++ ) {
            var max = segs1[i];
            var count = segCount[max];
            htmlStr += util.format('<tr><td>%d</td><td>%d</td></tr>', max, count);
        }
        
        for( var i=0; i<segs2.length; i++ ) {
            var max = segs2[i];
            var min = i > 0 ? segs2[i-1] : 0;

            var count = 0;
            for( var uid in paid ) {
                var cash = paid[uid];
                if( cash >= min && cash < max ) {
                    count += 1;
                }
            }

            htmlStr += util.format('<tr><td>%d</td><td>%d</td></tr>', max, count);
        }

        htmlStr += '</table>';

        res.end(htmlStr);
    }
}

function handle_pet_level(req, res) {
    res.writeHead(200, common.htmlHeaders);
    
    var allLevels = [];

    var serverGroup = +(req.server || 1);
    var startSid = 1 + (serverGroup-1)*100;
    var endSid = serverGroup * 100;

    var loader = new common.Loader(display);
    loader.addLoad(1);
    for( var sid in gDB.dbNames ) {
        if( sid < startSid || sid > endSid ) {
            continue;
        }
        loader.addLoad(1);
        calData(sid, function(){
            loader.onLoad(1);
        });
    }
    loader.onLoad(1);

    function calData(sid, callback) {

        var serverId = sid;
        var dbName = gDB.dbNames[serverId];
        var db = gDB.getDB(serverId);
        var dbUser = db.collection('user');

        dbUser.group(['pet.level'], {'pet':{$exists:true}}, {count:0}, "function(obj,prev){prev.count += 1;}", 
            function(err, results){
                var petLevels = {};
                results.forEach(function(obj){
                    petLevels[obj['pet.level']] = obj.count;
                });
                
                var levels = [sid];
                for( var i=1; i<=10; i++ ) {
                    levels.push(petLevels[i] || 0);
                }
                allLevels.push(levels);
                callback && callback(1);
        });
    }

    function display() {
        allLevels.sort(function(a,b){return a[0] - b[0]});
        var htmlStr = util.format('<h1>神兽等级分布 %d服-%d服</h1> <table border="1">', startSid, endSid) +
            '<tr>'+
            '<td><b>服务器编号</b></td>'+
            '<td><b>1级</b></td>'+
            '<td><b>2级</b></td>'+
            '<td><b>3级</b></td>'+
            '<td><b>4级</b></td>'+
            '<td><b>5级</b></td>'+
            '<td><b>6级</b></td>'+
            '<td><b>7级</b></td>'+
            '<td><b>8级</b></td>'+
            '<td><b>9级</b></td>'+
            '<td><b>10级</b></td>'+
            '</tr>';
        allLevels.forEach(function(obj){
            htmlStr += util.format('<tr><td>%d</td><td>%d</td><td>%d</td><td>%d</td><td>%d</td><td>%d</td><td>%d</td><td>%d</td><td>%d</td><td>%d</td><td>%d</td></tr>',
                                    obj[0], obj[1], obj[2], obj[3], obj[4], obj[5], obj[6], obj[7], obj[8], obj[9], obj[10]);
        });

        htmlStr += '</table>';

        res.end(htmlStr);
    }
}

function handle_role(req, res) {
    res.writeHead(200, common.htmlHeaders);
    
    var allDatas = [];

    var serverGroup = +(req.server || 1);
    var startSid = 1 + (serverGroup-1)*100;
    var endSid = serverGroup * 100;

    var loader = new common.Loader(display);
    loader.addLoad(1);
    for( var sid in gDB.dbNames ) {
        if( sid < startSid || sid > endSid ) {
            continue;
        }
        loader.addLoad(1);
        calData(sid, function(){
            loader.onLoad(1);
        });
    }
    loader.onLoad(1);

    function calData(sid, callback) {

        var serverId = sid;
        var dbName = gDB.dbNames[serverId];
        var db = gDB.getDB(serverId);
        var dbUser = db.collection('user');

        dbUser.find({'status.level':{$gte:55}}, {'role':1}).toArray(function(err, results){
                // soul_break, grouth_break , god, god2, god22
                var a = [];
                for( var i=0; i<55; i++ ) {
                    a[i] = 0;
                }

                results.forEach(function(obj){
                    var soul_break = 0;
                    var grouth_break = 0;
                    var god = 0;
                    var god2 = 0;
                    var god22 = 0;

                    var roles = obj.role;
                    for( var rid in roles ) {
                        var role = roles[rid];
                        if( role.grouth_break  ) {
                            grouth_break += 1;
                        }
                        if( role.soul_break ) {
                            soul_break += 1;
                        }

                        if( role.god == 1) {
                            god += 1;
                        }
                        if( role.soul >= 198 ) {
                            god2 += 1;
                        }
                        if( role.god == 2 ) {
                            god22 += 1;
                        }
                    }

                    a[soul_break] += 1;
                    a[11 + grouth_break] += 1;
                    a[22 + god] += 1;
                    a[33 + god2] += 1;
                    a[44 + god22] += 1;
                });

                a.unshift(sid);

                allDatas.push(a);
                callback && callback(1);
        });

    }

    function display() {
        allDatas.sort(function(a,b){return a[0] - b[0]});
        var htmlStr = util.format('<h1>武将数据分布 %d服-%d服</h1> <table border="1">', startSid, endSid) +
            '<tr>'+
            '<td><b>服务器编号</b></td>';
        for( var i=0; i<=10; i++ ) {
            htmlStr += util.format('<td><b>炼魂突破%s</b></td>', i);
        }
        for( var i=0; i<=10; i++ ) {
            htmlStr += util.format('<td><b>成长突破%s</b></td>', i);
        }
        for( var i=0; i<=10; i++ ) {
            htmlStr += util.format('<td><b>觉醒%s</b></td>', i);
        }
        for( var i=0; i<=10; i++ ) {
            htmlStr += util.format('<td><b>炼灵满%s</b></td>', i);
        }
        for( var i=0; i<=10; i++ ) {
            htmlStr += util.format('<td><b>入魔%s</b></td>', i);
        }

        htmlStr += '</tr>';
        
        allDatas.forEach(function(obj){
            var html = '<tr>';
            for( var i=0; i<55; i++ ) {
                html += util.format('<td>%s</td>', obj[i]);
            }

            html += '</tr>';
            htmlStr += html;
        });

        htmlStr += '</table>';

        res.end(htmlStr);
    }
}

function handle_wing(req, res) {
    res.writeHead(200, common.htmlHeaders);
    
    var allDatas = [];

    var serverGroup = +(req.server || 1);
    var startSid = 1 + (serverGroup-1)*100;
    var endSid = serverGroup * 100;

    var loader = new common.Loader(display);
    loader.addLoad(1);
    for( var sid in gDB.dbNames ) {
        if( sid < startSid || sid > endSid ) {
            continue;
        }
        loader.addLoad(1);
        calData(sid, function(){
            loader.onLoad(1);
        });
    }
    loader.onLoad(1);

    function calData(sid, callback) {

        var serverId = sid;
        var dbName = gDB.dbNames[serverId];
        var db = gDB.getDB(serverId);
        var dbUser = db.collection('user');

        dbUser.find({'status.level':{$gte:50}}, {'role':1}).toArray(function(err, results){
                var a = [];
                for( var i=0; i<=10; i++ ) {
                    a[i] = 0;
                }

                results.forEach(function(obj){

                    var roles = obj.role;
                    for( var rid in roles ) {
                        var role = roles[rid];
                        if( role.wing  ) {
                            a[role.wing] += 1;
                        }
                    }

                });

                a[0] = sid;

                allDatas.push(a);
                callback && callback(1);
        });

    }

    function display() {
        allDatas.sort(function(a,b){return a[0] - b[0]});
        var htmlStr = util.format('<h1>羽翼数据分布 %d服-%d服</h1> <table border="1">', startSid, endSid) +
            '<tr>'+
            '<td><b>服务器编号</b></td>';
        for( var i=1; i<=10; i++ ) {
            htmlStr += util.format('<td><b>羽翼%s</b></td>', i);
        }

        htmlStr += '</tr>';
        
        allDatas.forEach(function(obj){
            var html = '<tr>';
            for( var i=0; i<=10; i++ ) {
                html += util.format('<td>%s</td>', obj[i]);
            }

            html += '</tr>';
            htmlStr += html;
        });

        htmlStr += '</table>';

        res.end(htmlStr);
    }
}

function handle_ride(req, res) {
    res.writeHead(200, common.htmlHeaders);
    
    var allDatas = [];

    var serverGroup = +(req.server || 1);
    var startSid = 1 + (serverGroup-1)*100;
    var endSid = serverGroup * 100;

    var loader = new common.Loader(display);
    loader.addLoad(1);
    for( var sid in gDB.dbNames ) {
        if( sid < startSid || sid > endSid ) {
            continue;
        }
        loader.addLoad(1);
        calData(sid, function(){
            loader.onLoad(1);
        });
    }
    loader.onLoad(1);

    function calData(sid, callback) {

        var serverId = sid;
        var dbName = gDB.dbNames[serverId];
        var db = gDB.getDB(serverId);
        var dbUser = db.collection('user');

        dbUser.find({'status.level':{$gte:50}}, {'role':1, 'ride.horse':1}).toArray(function(err, results){
                var a = [];
                for( var i=0; i<31; i++ ) {
                    a[i] = 0;
                }

                results.forEach(function(obj){

                    var roles = obj.role;
                    var horses = (obj.ride ? obj.ride.horse : {});
                    for( var rid in roles ) {
                        var role = roles[rid];
                            
                        if( !role.ride  ) continue; 
                        var ride = role.ride;
                        var horse = horses[ride.horse];
                        if( gConfHorse[horse.horse_id].Quantity == 'orange' ) {
                            a[horse.level] += 1
                        }

                        a[10 + ride.seat] += 1;
                        a[20 + ride.whip] += 1;
                    }

                });

                a[0] = sid;

                allDatas.push(a);
                callback && callback(1);
        });

    }

    function display() {
        allDatas.sort(function(a,b){return a[0] - b[0]});
        var htmlStr = util.format('<h1>坐骑数据分布 %d服-%d服</h1> <table border="1">', startSid, endSid) +
            '<tr>'+
            '<td><b>服务器编号</b></td>';
        for( var i=1; i<=10; i++ ) {
            htmlStr += util.format('<td><b>橙马%s</b></td>', i);
        }

        for( var i=1; i<=10; i++ ) {
            htmlStr += util.format('<td><b>马鞍%s</b></td>', i);
        }

        for( var i=1; i<=10; i++ ) {
            htmlStr += util.format('<td><b>马鞭%s</b></td>', i);
        }

        htmlStr += '</tr>';
        
        allDatas.forEach(function(obj){
            var html = '<tr>';
            for( var i=0; i< 31; i++ ) {
                html += util.format('<td>%s</td>', obj[i]);
            }

            html += '</tr>';
            htmlStr += html;
        });

        htmlStr += '</table>';

        res.end(htmlStr);
    }
}

function getIntervalMap(data) {
    var intervals = { };

    for( var i=1; i<600; i+=100 ) {
        intervals[i + '-' + parseInt(i+99)] = 0;
    }

    intervals['601-1000'] = 0;

    for( var i=1001; i<2000; i+=500 ) {
        intervals[i + '-' + parseInt(i+499)] = 0;
    }

    for( var i=2001; i<5000; i+=1000 ) {
        intervals[i + '-' + parseInt(i+999)] = 0;
    }

    intervals['5001-8000'] = 0;
    intervals['8001-12000'] = 0;
    intervals['12001-20000'] = 0;
    intervals['20001-30000'] = 0;
    intervals['30001-50000'] = 0;
    intervals['50001-80000'] = 0;
    intervals['80001-100000'] = 0;
    intervals['100000-100000000'] = 0;

    if( !data ) return intervals;

    for( var uid in data ) {
        var cash = data[uid];
        if( cash <= 0 ) continue;
        // 区间上限
        var delimiter = 0;
        if( cash <= 600 ) {
            delimiter = Math.ceil(cash/100) * 100 ;
            intervals[parseInt(delimiter-100+1) + '-' + delimiter] ++;
        }else if( cash <= 1000 ) {
            intervals['601-1000'] ++;
        }else if( cash <= 2000 ) {
            delimiter = Math.ceil(cash/500) * 500 ;
            intervals[parseInt(delimiter-500+1) + '-' + delimiter] ++;
        }else if( cash <= 5000 ) {
            delimiter = Math.ceil(cash/1000) * 1000 ;
            intervals[parseInt(delimiter-1000+1) + '-' + delimiter] ++;
        }else if( cash <= 8000 ) {
            intervals['5001-8000'] ++;
        }else if( cash <= 12000 ) {
            intervals['8001-12000'] ++;
        }else if( cash <= 20000 ) {
            intervals['12001-20000'] ++;
        }else if( cash <= 30000 ) {
            intervals['20001-30000'] ++;
        }else if( cash <= 50000 ) {
            intervals['30001-50000'] ++;
        }else if( cash <= 80000 ) {
            intervals['50001-80000'] ++;
        }else if( cash <= 100000 ) {
            intervals['80001-100000'] ++;
        }else{
            intervals['100000-100000000'] ++;
        }
    }
    
    return intervals;
}

function handle_grouth(query, res){
    var content = 'error';
    var getUserInfoAndShow = function(uid) {
        dbUser.findOne({_id: uid}, {'active_time':1, 'mark':1, 'info':1, status:1, role:1, team:1, equip:1, pet:1}, function(err, result) {
            if( err || !result ) return;
            var status = result.status;
            var mark = result.mark;
            var info = result.info;
            var activeTime = result.active_time;
            var petLevel = result.pet ? result.pet.level : 0;
            var rolesHas = result.role;
            var team = result.team;
            var equips = result.equip;
            var roles = {};
            var htmlStr = '<table border=1>' +
                '<tr>'+
                    '<td><b>武将</b></td>'+
                    '<td><b>兵种</b></td>'+
                    '<td><b>战力</b></td>'+
                    '<td><b>等级</b></td>'+
                    '<td><b>炼魂</b></td>'+
                    '<td><b>培养</b></td>'+
                    '<td><b>装备</b></td>'+
                    '<td><b>强化</b></td>'+
                    '<td><b>宝石</b></td>'+
                    '<td><b>装备星级</b></td>'+
                    '<td><b>技能</b></td>'+
                    '<td><b>炼魂突破</b></td>'+
                    '<td><b>培养突破</b></td>'+
                    '<td><b>觉醒</b></td>'+
                    '<td><b>入魔</b></td>'+
                '</tr>';
            for( var pos in team ) {
                var rid = team[pos];
                if( !rid ) continue;
                roles[rid] = rolesHas[rid];

                var rname = gConfRole[rid].Name;
                var role = roles[rid];
                var fightForce = role.fight_force;
                var level = role.level;
                var soul = role.soul;
                var grouth = role.grouth;
                var roleEquips = [role.weapon, role.armor, role.accessory];
                var equipsLevel = '';
                var equipsStrength = '';
                var gemsLevelStr = '';
                var equipStar = '';
                for( var i=0, len=roleEquips.length; i<len; i++ ) {
                    var equip = equips[roleEquips[i]];
                    var gemLevel = '';
                    if( !equip ) {
                        equipsLevel += 0;
                        equipsStrength += 0;
                        equipStar += 0;
                        gemLevel = '0:0:0';
                    }else{
                        equipsLevel += equip.level; 
                        equipsStrength += equip.strength;
                        equipStar += (equip.star || 0);
                        var gems = equip.gems;
                        var gemsCount = 0;
                        var gemTotal = Object.keys(gems).length;
                        for( var slot in gems ) {
                            gemLevel += gems[slot]; 
                            if( gemsCount < gemTotal-1 ) {
                                gemLevel += ':';
                            }
                            gemsCount ++;
                        }
                    }

                    gemsLevelStr += gemLevel;
                    if( i < len-1 ) {
                        equipsLevel += ',';
                        equipsStrength += ',';
                        gemsLevelStr += ',';
                        equipStar += ',';
                    }
                }
                var soldierKind = soldierMap[gConfRole[rid].Soldier];
                var skills = role.skill_levels;
                var skillsLevel = '';
                var skillsSeq = [2,4,1,3,5];
                for( var i=0, len=skillsSeq.length; i<len; i++ ) {
                    var skillId = skillsSeq[i];
                    skillsLevel += skills[skillId] || 0;
                    if( i < len-1 ) {
                        skillsLevel += ',';
                    }
                }

                htmlStr += '<tr>'+
                        '<td>'+rname+'</td>'+
                        '<td>'+soldierKind+'</td>'+
                        '<td>'+fightForce+'</td>'+
                        '<td>'+level+'</td>'+
                        '<td>'+soul+'</td>'+
                        '<td>'+grouth+'</td>'+
                        '<td>'+equipsLevel+'</td>'+
                        '<td>'+equipsStrength+'</td>'+
                        '<td>'+gemsLevelStr+'</td>'+
                        '<td>'+equipStar+'</td>'+
                        '<td>'+skillsLevel+'</td>'+
                        '<td>'+(role.soul_break || 0)+'</td>'+
                        '<td>'+(role.grouth_break || 0)+'</td>'+
                        '<td>'+(role.god > 0 ? 1 : 0)+'</td>'+
                        '<td>'+(role.god == 2 ? 1 : 0)+'</td>'+
                    '</tr>';
            }

            htmlStr += '</table>';

            var htmlStr1 = '<table border=1>' +
                '<tr>'+
                    '<td><b>玩家名</b></td>'+
                    '<td><b>总充值</b></td>'+
                    '<td><b>君主等级</b></td>'+
                    '<td><b>战斗力</b></td>'+
                    '<td><b>金币</b></td>'+
                    '<td><b>粮草</b></td>'+
                    '<td><b>军功</b></td>'+
                    '<td><b>元宝</b></td>'+
                    '<td><b>神兽等级</b></td>'+
                    '<td><b>创建时间</b></td>'+
                    '<td><b>上次登录</b></td>'+
                    '<td><b>登录天数</b></td>'+
                '</tr>';
            var dbPay = gDB.getDB(serverId).collection('pay');
            var cursor = dbPay.find({openid: query.openid }, {'amt':1});
            var paySum = 0;
            cursor.each(function(err, item){
                if( err ) {
                    done('error');
                }
                if( cursor.isClosed() ) {
                    paySum = Math.round(paySum);
                    var createTime = new Date((+info.create)*1000);
                    var createTimeString = "" + createTime.getFullYear() + "-" + (createTime.getMonth()+1) + "-" + createTime.getDate();
                    var lastTime = new Date((info.create + activeTime)* 1000);
                    var lastTimeString = "" + lastTime.getFullYear() + "-" + (lastTime.getMonth()+1) + "-" + lastTime.getDate();
                    htmlStr1 += '<tr>'+
                            '<td>'+info.un+'</td>'+
                            '<td>'+paySum+'</td>'+
                            '<td>'+status.level+'</td>'+
                            '<td>'+status.fight_force+'</td>'+
                            '<td>'+status.gold+'</td>'+
                            '<td>'+status.food+'</td>'+
                            '<td>'+status.honor+'</td>'+
                            '<td>'+status.cash+'</td>'+
                            '<td>'+petLevel+'</td>'+
                            '<td>'+createTimeString+'</td>'+
                            '<td>'+lastTimeString+'</td>'+
                            '<td>'+mark.login_days+'</td>'+
                        '</tr>';
                    htmlStr1 += '</table>';

                    htmlStr1 += '<p></p>';
                    htmlStr1 += htmlStr;
                    done(htmlStr1);
                }else{
                    paySum += item.amt/100; 
                }
            });
        });
    };

    var done = function(content) {
        res.writeHead(200, common.htmlHeaders);
        res.end(content);
    };

    do {
        if( isNaN(query.server) || !query.openid ) {
            break;
        }

        var serverId = +query.server;
        var dbName = gDB.dbNames[serverId];
        if( !dbName ) {
            break;
        }

        var dbPlat = gDB.getDB(serverId).collection('plat');
        var dbUser = gDB.getDB(serverId).collection('user');
        dbPlat.findOne({_id:query.openid}, {}, function(err, result){
            if( err || !result ) {
                done(content);
                return;
            }
            getUserInfoAndShow(result.uid);
        });

        return;
    }while(false);

    done(content);
}

function handle_cash_log(req, res){
    var content = 'error';
    res.writeHead(200, common.htmlHeaders);
    if( !req.sid || !req.openid) {
        res.end(content);
        return;
    }

    var preMonth = +req.month;
    var serverId = +req.sid;
    var dbName = gDB.dbNames[serverId];
    if( !dbName ) {
        res.end(content);
        return;
    }

    var data = [];
    var done = function(){
        var htmlStr = '<h1>'+req.sid+'服消费记录</h1> <table border="1">' +
            '<tr>'+
            '<td><b>时间</b></td>'+
            '<td><b>行为</b></td>'+
            '<td><b>元宝</b></td>'+
            '</tr>';

        for( var i=0, len=data.length; i<len; i++ ) {
            htmlStr += ' <tr>'+
                '<td>'+ data[i].time + '</td>'+
                '<td>'+ data[i].action + '</td>'+
                '<td>'+ data[i].cash + '</td>'+
                '</tr>';
        }

        htmlStr += '</table>';

        res.end(htmlStr);
    };

    var dbAnalysis = gDB.getDB(serverId).collection('analysis');
    var dbPlat = gDB.getDB(serverId).collection('plat');
    dbPlat.findOne({_id:req.openid}, {}, function(err, result){
        if( err || !result ) {
            res.end(content);
            return;
        }

        var endDate = '';
        var startDate = '';
        if (req.start_time && req.end_time) {
            startDate = req.start_time + ' 00:00:00';
            endDate = req.end_time + ' 00:00:00';
        } else {
            endDate = new Date();
            startDate = common.getLastMonthSameDate(endDate);
            endDate = endDate.format('yyyy-MM-dd hh:mm:ss');
            startDate = startDate.format('yyyy-MM-dd hh:mm:ss');
        }

        dbAnalysis.find({uid: result.uid, 'date':{'$gte':startDate, '$lt':endDate}}).sort([['date', -1]]).toArray(function(err, items) {
            for( var i=0, len=items.length; i<len; i++ ) {
                data.push({time: items[i].date, action: items[i].action, cash: items[i].cash});
            }

            done();
        });
    });
}

function handle_recover(query, res){
    var content = '参数错误';
    do {
        if( isNaN(query.sid) ) {
            break;
        }

        var serverId = +query.sid;

        last = exec(util.format('%s/game recover %d', toolPath, serverId));

        var allData = '';
        var failed = false;
        last.stdout.on('data', function (data) {
            allData += data;
        });

        last.stderr.on('data', function(data) {
            allData += data;
            failed = true;
        });

        last.on('exit', function (code) {
            res.writeHead(200, common.htmlHeaders);
            if (code || failed) {
                res.end('复活失败');
            } else {
                res.end('复活成功');
            }
        });

        return;
    }while(false);

    res.writeHead(200, common.htmlHeaders);
    res.end(content);
}

function handle_tools(query, res){
    var content = '参数错误';
    do {
        if (isNaN(query.sid)) {
            break;
        }

        var serverId = +query.sid;
        var cmd = "";
        
        if (query.recover) {
            cmd = util.format('%s/game recover %d', toolPath, serverId);
        } else if (query.restart) {
            cmd = util.format('%s/game frestart %d', toolPath, serverId);
        } else if (query.mem) {
            cmd = util.format('%s/game mem %d', toolPath, serverId);
        } else if (query.top10) {
            hostId = config.server.query('game', serverId);
            if (!hostId) {
                content = "query game host failed";
                break;
            }
            cmd = util.format('%s/host top %s node', toolPath, hostId);
        } else {
            content = "unknown command";
            break;
        }


        last = exec(cmd);

        var allData = '';
        var failed = false;
        last.stdout.on('data', function (data) {
            allData += data;
        });

        last.stderr.on('data', function(data) {
            allData += data;
            failed = true;
        });

        last.on('exit', function (code) {
            res.writeHead(200, common.htmlHeaders);
            if (code || failed) {
                res.end(util.format("操作失败:\n %j", failed));
            } else {
                allData = allData.split('\n');
                if (query.mem) {
                    htmlLines = [];

                    for (var line = 0, total = allData.length; line < total; ++line) {
                        var lineContent = allData[line].split(' ');
                        var items = [];
                        for (var i = 0, len = lineContent.length; i < len; ++i) {
                            if (lineContent[i].length > 0) {
                                items.push(lineContent[i]);
                            }
                        }

                        var itemCount = items.length;
                        if (itemCount < 4) {
                            continue;
                        }

                        htmlLines.push(util.format('PID:%s, CPU:%d%%, MEM:%d%%', items[1], items[itemCount-4], items[itemCount-3]));
                    }

                    res.end(htmlLines.join('<br/>'));
                    return;

                } else if (query.top10) {
                    htmlLines = [];

                    for (var line = 1, total = allData.length; line < total; ++line) {
                        var lineContent = allData[line].split(' ');
                        var items = [];
                        for (var i = 0, len = lineContent.length; i < len; ++i) {
                            if (lineContent[i].length > 0) {
                                items.push(lineContent[i]);
                            }
                        }

                        var itemCount = items.length;
                        if (itemCount < 4) {
                            continue;
                        }

                        htmlLines.push(util.format('PID:%s, CPU:%d%%, MEM:%d%%', items[0], items[itemCount-4], items[itemCount-3]));
                    }

                    res.end(htmlLines.join('<br/>'));
                    return;

                }
                res.end(allData.join('<br/>'));
            }
        });

        return;
    }while(false);

    res.writeHead(200, common.htmlHeaders);
    res.end(content);
}

var cashMap = {
    'all': '全部',
    'buy_food': '购买粮食',
    'buy_gold':'购买金币',
    'mine_occupy': '购买金矿次数',
    'reset_kill': '重置过关斩将',
    'reset_tower': '重置千重楼',
    'rank_fight': '擂台次数',
    'tavern_invite': '元宝招将',
    'prison': '抓捕和压榨',
    'upgrade_seat': '升级训练位',
    'clear_all_seat': '清除全部训练CD',
    'clear_seat': '清除训练CD',
    'role_refine': '洗髓',
    'rank_challenge_topone': '天下第一',
    'league_contribute': '军团贡献',
    'shop': '商城',
    'business_go': '跑商',
    'role_transfer': '传功',
    'league_create':'创建军团',
    'activity_invest_join': '开服投资',
    'activity_limit_buy': '购买限量商品',
    'surprise_go': '步步惊喜',
    'black_shop': '黑市',
    'activity_start_wheel': '命运之轮',
    'boss_inspire': 'BOSS鼓舞',
    'boss_rebirth': 'BOSS重生',
    'league_impeach': '军团弹劾',
    'league_war_buy_move': '军团战迁徙',
    'league_war_buy_energy': '军团战购买体力',
    'rank_fight': '擂台战斗',
    'rank_share_tv': '貂蝉TV',
    'tomb_fight': '古墓挑战',
    'tomb_dig': '古墓挖宝',
    'offline_xp': '修炼打坐',
    'rank_challenge_topone2': '疯狂擂台',
    'activity_buy_feed': '回馈礼包',
    'forge': '神兵锻造',
    'avtivity_wish': '祈福许愿',
    'activity_western_go': '西域夺宝',
    'activity_group_on': '百服团购',
    'activity_gem_buy': '宝石放送',
    'my_limit_buy': '个人限量购买',
    'activity_role_send':'神将放送',
    'cloud_shop':'云购',
    'role_upgrade_grouth':'至尊培养',
    'break_egg':'砸蛋',
    'huatuo_promote':'华佗炼丹提炼',
    'huatuo_make':'华佗炼丹元宝炼丹',
    'activity_chargegod_limit_buy':'神将限量购买',
    'activity_charge_limit_buy':'充值限量购买',
    'activity_diamond_card_open':'钻石卡牌翻牌',
    'troop_match_clear_time':'战术大赛清CD',
    'battle_world_buy_energy':'决战天下买体力',
    'break_egg_clear':'砸蛋清CD',
    'diy_special_role':'自定义本命武将',
    'activity_diamond_card_refresh':'钻石卡牌刷新',
    'battle_world_buy_move':'决战天下购买移动次数',
    'troop_match_retry':'战术大赛重打',
    'activity_super_card_open':'超级卡牌',
    'activity_level_invest':'升级投资',
    'activity_login_invest':'登录投资',
    'activity_super_card_refresh':'超级卡牌刷新',
    'activity_mystic_shop_buy':'神秘商店购买',
    'activity_buy_axe_dig_treasure':'购买地下宝藏铁镐',
    'blessdice_change_luck':'福星高照改运',
    'activity_vip_box_buy':'购买特权宝箱',
    'reset_mybattle':'个人战役重置',
    'lottery_select_lottery_number':'貂蝉大乐透选号',
    'activity_reset_dig_treasure':'地下宝藏重置',
};

var soldierMap = {
    1: '刀',
    2: '枪',
    3: '骑',
    4: '谋',
    5: '红',
};
